package com.moonhill.bt;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Vector;

import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

public abstract class BTClient implements Runnable, DiscoveryListener,
		DataReaderListener {
	private static final int[] SEARCH_ATTRSET = { BTServer.ATTR_PROVIDER_NAME };
	//public static final String SAY_HELLO = "moon";
	//public static final String JOIN_FLAG = "IWJMH=";
	private LocalDevice localDevice;
	private DiscoveryAgent discoveryAgent;

	private BTConfig config;

	private Vector servers = new Vector();
	private StreamConnection conn;
	private DataOutputStream dos;
	private DataReader dataReader;

	private ServerObject selectedSo;

	private String localName;
	private boolean discard;

	public BTClient(BTConfig config) {
		this.config = config;
	}

	public void startSearchGameServer() {
		clearBt();
		new Thread(this).start();
	}

	public boolean connect2GameServer(int index) {
		int size = servers.size();
		if (0 <= index && index < size) {
			ServerObject so = (ServerObject) servers.elementAt(index);
			if (so.providerName != null) {
				new ConnectioinThread(so);
				return true;
			} else {
				return false;
			}

		} else {
			return false;
		}
	}

	public void clearBt() {
		
		servers.removeAllElements();
		localName = null;
		discoveryAgent = null;
		localDevice = null;
		System.gc();
	}

	public synchronized void run() {
		boolean searchOk = true;

		try {
			localDevice = LocalDevice.getLocalDevice();
			localName = localDevice.getFriendlyName();
			discoveryAgent = localDevice.getDiscoveryAgent();
			servers.removeAllElements();
			searchOk = discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);
		} catch (Exception e) {
			e.printStackTrace();
			searchOk = false;
		}
		if (!searchOk) {
			clearBt();
			onSearchCompleted(null, false);
			return;
		}

		try {
			// ����,��inquiryCompleted()�ص������
			this.wait();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		if (servers.size() == 0) {
			clearBt();
			onSearchCompleted(null, true);
			return;
		}

		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			RemoteDevice rd = so.device;
			try {
				UUID[] uuidSet = new UUID[2];
				uuidSet[0] = new UUID(0x1101);
				uuidSet[1] = new UUID(BTServer.BTID, false);
				so.transID = discoveryAgent.searchServices(SEARCH_ATTRSET,
						uuidSet, rd, this);
				System.out.println("so.transID = " + so.transID);
			} catch (Exception e) {
				e.printStackTrace();
				continue;

			}

		}

		try {
			System.out.println("before block");
			this.wait();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		System.out.println("after block");
		Vector v = new Vector();
		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			if (so.providerName != null) {
				v.addElement(so.providerName);
			}
		}
		if (v.size() == 0) {
			clearBt();
			onSearchCompleted(null, true);
			return;
		}
		String[] hosts = new String[v.size()];
		for (int i = 0; i < hosts.length; i++) {
			hosts[i] = (String) v.elementAt(i);
		}
		v = null;
		onSearchCompleted(hosts, true);

	}

	public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			if (so.device.equals(btDevice)) {
				return;
			}
		}
		ServerObject so = new ServerObject(btDevice);
		servers.addElement(so);

	}

	public void inquiryCompleted(int discType) {

		synchronized (this) {
			this.notifyAll();
		}

	}

	public void serviceSearchCompleted(int transID, int respCode) {
		// ������е��豸���Ѿ����������ϣ����ѳ�ʼ���̡߳�
		System.out.println("jjjjjjjjjjjjjjjjjjjjj,transID = " + transID + ","
				+ respCode);
		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			if (so.transID == transID) {
				so.transID = -1;
				break;
			}
		}
		boolean finished = true;
		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			if (so.transID >= 0) {
				finished = false;
				break;
			}
		}

		if (finished) {
			synchronized (this) {
				this.notifyAll();
			}
		}

	}

	public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
		for (int i = 0; i < servers.size(); i++) {
			ServerObject so = (ServerObject) servers.elementAt(i);
			if (so.transID == transID) {
				so.transID = -1;
				for (int j = 0; j < servRecord.length; j++) {
					ServiceRecord sr = servRecord[j];
					try {
						DataElement elm = sr
								.getAttributeValue(SEARCH_ATTRSET[0]);
						if (elm != null
								&& elm.getDataType() == DataElement.STRING) {
							String s = (String) elm.getValue();
							s = new String(BTUtil.hexStrToBytes(s), "UTF-8");
							so.providerName = s;
							so.sr = sr;
							break;
						} else {
							System.out.println("ffffffffffffffffffffffffffuck");
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}
				break;
			}
		}
	}

	class ConnectioinThread extends Thread {
		private ServerObject so;

		public ConnectioinThread(ServerObject so) {
			this.so = so;
			this.start();
		}

		public void run() {
			try {
				String url = so.sr.getConnectionURL(
						ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
				conn = (StreamConnection) Connector.open(url);
				DataInputStream dis = conn.openDataInputStream();
				dos = conn.openDataOutputStream();
				if (conn != null && dis != null && dos != null) {
					// when connected,if send the localName to the server;
					String myName = getMyName();
					String ln = myName == null ? localName : myName;
					dos.writeUTF(ln);
					dos.flush();
					dataReader = new DataReader(dis, BTClient.this);
					onConnectionCompleted(true, "OK");
				}

			} catch (Exception e) {

				// TODO Auto-generated catch block
				e.printStackTrace();

				if (dos != null) {
					try {
						dos.close();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					dos = null;
				}
				if (dataReader != null) {
					dataReader.cancel();
					dataReader = null;
				}
				if (conn != null) {
					try {
						conn.close();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					conn = null;
				}

				onConnectionCompleted(false, e.getMessage());

			}
		}
	}

	public void disConnetion() {

		discard = true;
		this.dataReader.cancel();
		dataReader = null;

		try {
			dos.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		dos = null;

		try {
			conn.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		conn = null;
		System.gc();

	}

	public void sendMsg(byte[] msg) {
		try {
			byte[] btData = BTGame.getBTMsg(msg);
			System.out.println(" in BTClient.sendMsg,bt msg len = "
					+ btData.length);
			dos.write(btData);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void onConnectionError(DataReader reader) {
		if (!discard) {
			onServerDrop();
		}
	}

	class ServerObject {
		RemoteDevice device;
		String providerName;
		int transID;
		ServiceRecord sr;

		public ServerObject(RemoteDevice rd) {
			this.device = rd;
		}

	}

	public void onReceiveData(byte[] msg) {
		System.out.println("in BTClient.onReceiveData");
		onReceivePlayerMsg(msg);
	}

	// when search progress is completed,this method is called.
	public abstract void onSearchCompleted(String[] remoteDeviceNames,
			boolean isOk);

	public abstract void onServerDrop();

	public abstract String getMyName();

	public abstract void onReceivePlayerMsg(byte[] msg);

	public abstract void onConnectionCompleted(boolean ok, String info);
}
